package com.jackchong.utils;

import android.app.Activity;
import android.graphics.Path;
import android.graphics.PathMeasure;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.SystemClock;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

import com.jackchong.utils.ThreadUtils;

import java.lang.ref.WeakReference;


/**
 * author : jack(黄冲)
 * e-mail : 907755845@qq.com
 * create : 2020/11/4
 * desc   :
 */
public class GestureTouchUtils {

    public static int HIGH = 10;
    public static int NORMAL = 100;
    public static int LOW = 1000;

    private static long DEFAULT_DURATION = 2000;


    /**
     * 模拟手指触摸操作
     *
     * @param view 一般为 ViewGroup
     */
    public static void simulateClick(View view, float x, float y) {
        dealSimulateClick(view, x, y);
    }

    /**
     * 模拟手指触摸操作
     *
     * @param activity 当前 Activity
     */
    public static void simulateClick(Activity activity, float x, float y) {
        dealSimulateClick(activity, x, y);
    }

    /**
     * 模拟手势滑动
     *
     * @param view   滑动的 view
     * @param startX 起始位置 x
     * @param startY 起始位置 y
     * @param endX   终点位置 x
     * @param endY   终点位置 y
     */
    public static void simulateScroll(View view, int startX, int startY, int endX, int endY) {
        simulateScroll(view, startX, startY, endX, endY, DEFAULT_DURATION);
    }

    /**
     * 模拟手势滑动
     *
     * @param activity 当前的 activity
     * @param startX   起始位置 x
     * @param startY   起始位置 y
     * @param endX     终点位置 x
     * @param endY     终点位置 y
     */
    public static void simulateScroll(Activity activity, int startX, int startY, int endX, int endY) {
        simulateScroll(activity, startX, startY, endX, endY, DEFAULT_DURATION);
    }

    /**
     * 模拟手势滑动
     *
     * @param view     滑动的 view
     * @param startX   起始位置 x
     * @param startY   起始位置 y
     * @param endX     终点位置 x
     * @param endY     终点位置 y
     * @param duration 滑动时长 单位：ms
     */
    public static void simulateScroll(View view, int startX, int startY, int endX, int endY, long duration) {
        simulateScroll(view, startX, startY, endX, endY, duration, NORMAL);
    }

    /**
     * 模拟手势滑动
     *
     * @param activity 当前的 activity
     * @param startX   起始位置 x
     * @param startY   起始位置 y
     * @param endX     终点位置 x
     * @param endY     终点位置 y
     * @param duration 滑动时长 单位 ms
     */
    public static void simulateScroll(Activity activity, int startX, int startY, int endX, int endY, long duration) {
        simulateScroll(activity, startX, startY, endX, endY, duration, NORMAL);
    }

    /**
     * 模拟手势滑动
     *
     * @param view     滑动的 view
     * @param startX   起始位置 x
     * @param startY   起始位置 y
     * @param endX     终点位置 x
     * @param endY     终点位置 y
     * @param duration 滑动时长 单位：ms
     * @param period   滑动周期
     *                 {@link #LOW} 慢
     *                 {@link #NORMAL} 正常
     *                 {@link #HIGH} 高
     */
    public static void simulateScroll(View view, int startX, int startY, int endX, int endY, long duration, int period) {
        dealSimulateScroll(view, startX, startY, endX, endY, duration, period);
    }

    /**
     * 模拟手势滑动
     *
     * @param activity 当前的 activity
     * @param startX   起始位置 x
     * @param startY   起始位置 y
     * @param endX     终点位置 x
     * @param endY     终点位置 y
     * @param duration 滑动时长 单位 ms
     * @param period   滑动周期
     *                 {@link #LOW} 慢
     *                 {@link #NORMAL} 正常
     *                 {@link #HIGH} 高
     */
    public static void simulateScroll(Activity activity, float startX, float startY, float endX, float endY, long duration, int period) {
        dealSimulateScroll(activity, startX, startY, endX, endY, duration, period);
    }

    private static void dealSimulateScroll(Object object, float startX, float startY, float endX, float endY, long duration, int period) {
        long downTime = SystemClock.uptimeMillis();
        Handler handler;
        if (object instanceof View) {
            View view = (View) object;
            handler = new ViewHandler(view);
            view.onTouchEvent(MotionEvent.obtain(downTime, downTime, MotionEvent.ACTION_DOWN, startX, startY, 0));
            CoordBean bean = new CoordBean(startX, startY, endX, endY, duration, period);
            Message.obtain(handler, 1, bean).sendToTarget();
        } else if (object instanceof Activity) {
            Activity activity = (Activity) object;
            handler = new ActivityHandler(activity);
            activity.dispatchTouchEvent(MotionEvent.obtain(downTime, downTime, MotionEvent.ACTION_DOWN, startX, startY, 0));
            CoordBean bean = new CoordBean(startX, startY, endX, endY, duration, period);
            Message.obtain(handler, 1, bean).sendToTarget();
        }
    }


    public static void dealSimulateScroll(Object object, Path path, long duration, int period) {

        Handler handler;
        if (object instanceof View) {
            View view = (View) object;
            handler = new ViewHandler(view);
            PathBean bean = new PathBean(path, duration, period);
            Message.obtain(handler, 1, bean).sendToTarget();
        } else if (object instanceof Activity) {
            Activity activity = (Activity) object;
            handler = new PathHandler(activity);
            PathBean bean = new PathBean(path, duration, period);
            Message.obtain(handler, 1, bean).sendToTarget();
        }
    }


    /**
     * 处理手势点击的操作
     */
    private static void dealSimulateClick(Object object, float x, float y) {
        MotionEvent downEvent = MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), MotionEvent.ACTION_DOWN, x, y, 0);
        MotionEvent upEvent = MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, x, y, 0);
        if (object instanceof View) {
            ((View) object).onTouchEvent(downEvent);
            ((View) object).onTouchEvent(upEvent);
        } else if (object instanceof Activity) {
            ((Activity) object).dispatchTouchEvent(downEvent);
            ((Activity) object).dispatchTouchEvent(upEvent);
        }
        downEvent.recycle();
        upEvent.recycle();
    }

    public static void dealSimulateDoubleClick(Object object, float x, float y) {
        MotionEvent downEvent = MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), MotionEvent.ACTION_DOWN, x, y, 0);
        MotionEvent upEvent = MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, x, y, 0);

        if (object instanceof View) {
            ((View) object).onTouchEvent(downEvent);
            ((View) object).onTouchEvent(upEvent);

            ThreadUtils.postDelay(200, () -> {
                MotionEvent downEvent2 = MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), MotionEvent.ACTION_DOWN, x, y, 0);
                MotionEvent upEvent2 = MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, x, y, 0);
                ((View) object).onTouchEvent(downEvent2);
                ((View) object).onTouchEvent(upEvent2);
                downEvent2.recycle();
                upEvent2.recycle();
            });
        } else if (object instanceof Activity) {
            ((Activity) object).dispatchTouchEvent(downEvent);
            ((Activity) object).dispatchTouchEvent(upEvent);

            ThreadUtils.postDelay(200, () -> {
                MotionEvent downEvent2 = MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), MotionEvent.ACTION_DOWN, x, y, 0);
                MotionEvent upEvent2 = MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, x, y, 0);
                ((Activity) object).dispatchTouchEvent(downEvent2);
                ((Activity) object).dispatchTouchEvent(upEvent2);
                downEvent2.recycle();
                upEvent2.recycle();
            });
        }
        downEvent.recycle();
        upEvent.recycle();
    }


    static class ViewHandler extends Handler {
        WeakReference<View> mView;

        ViewHandler(View activity) {
            super(Looper.getMainLooper());
            mView = new WeakReference<>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            View theView = mView.get();
            if (theView == null || !theView.isAttachedToWindow()) {
                return;
            }
            long downTime = SystemClock.uptimeMillis();
            CoordBean bean = (CoordBean) msg.obj;
            long count = bean.count;
            if (count >= bean.totalCount) {
                theView.onTouchEvent(MotionEvent.obtain(downTime, downTime, MotionEvent.ACTION_UP, bean.endX, bean.endY, 0));
            } else {
                theView.onTouchEvent(MotionEvent.obtain(downTime, downTime, MotionEvent.ACTION_MOVE, bean.startX + bean.ratioX * count, bean.startY + bean.ratioY * count, 0));
                bean.count++;
                Message message = new Message();
                message.obj = bean;
                sendMessageDelayed(message, bean.period);
            }
        }
    }

    static class ActivityHandler extends Handler {
        WeakReference<Activity> mActivity;

        ActivityHandler(Activity activity) {
            super(Looper.getMainLooper());
            mActivity = new WeakReference<>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            Activity theActivity = mActivity.get();
            if (theActivity == null || theActivity.isFinishing()) {
                return;
            }
            long downTime = SystemClock.uptimeMillis();
            CoordBean bean = (CoordBean) msg.obj;
            long count = bean.count;
            if (count >= bean.totalCount) {
                theActivity.dispatchTouchEvent(MotionEvent.obtain(downTime, downTime, MotionEvent.ACTION_UP, bean.endX, bean.endY, 0));
            } else {
                theActivity.dispatchTouchEvent(MotionEvent.obtain(downTime, downTime, MotionEvent.ACTION_MOVE, bean.startX + bean.ratioX * count, bean.startY + bean.ratioY * count, 0));
                bean.count++;
                Message message = new Message();
                message.obj = bean;
                sendMessageDelayed(message, bean.period);
            }
        }
    }


    static class PathHandler extends Handler {
        WeakReference<Activity> mActivity;
        private float[] mPos = new float[2];
        PathMeasure mPathMeasure = new PathMeasure();


        PathHandler(Activity activity) {
            super(Looper.getMainLooper());
            mActivity = new WeakReference<>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            Activity theActivity = mActivity.get();
            if (theActivity == null || theActivity.isFinishing()) {
                return;
            }
            long downTime = SystemClock.uptimeMillis();
            PathBean bean = (PathBean) msg.obj;
            long count = bean.count;

            mPathMeasure.setPath(bean.path, false);
            mPathMeasure.getPosTan(mPathMeasure.getLength() / bean.totalCount * bean.count, mPos, null);

            Log.e("test", mPos[0] + "  ," + mPos[1] + "    " + count + "    " + bean.totalCount);
            if (count == 0){
                theActivity.dispatchTouchEvent(MotionEvent.obtain(downTime, downTime, MotionEvent.ACTION_DOWN, mPos[0], mPos[1], 0));
            } else if (count >= bean.totalCount) {
                theActivity.dispatchTouchEvent(MotionEvent.obtain(downTime, downTime, MotionEvent.ACTION_UP, mPos[0], mPos[1], 0));
                return;
            } else {
                theActivity.dispatchTouchEvent(MotionEvent.obtain(downTime, downTime, MotionEvent.ACTION_MOVE, mPos[0], mPos[1], 0));
            }
            bean.count++;
            Message message = new Message();
            message.obj = bean;
            sendMessageDelayed(message, bean.period);
        }
    }


    static class PathBean {

        Path path;

        /**
         * 总共周期
         */
        long totalCount;
        /**
         * 当前周期
         */
        long count = 0;
        int period = NORMAL;

        PathBean(Path path, long duration, int speed) {
            this.path = path;
            this.period = speed;
            totalCount = duration / speed;
        }
    }


    static class CoordBean {

        /**
         * 起始位置 X
         */
        float startX;
        /**
         * 起始位置 Y
         */
        float startY;
        /**
         * 终点位置 X
         */
        float endX;
        /**
         * 终点位置 Y
         */
        float endY;
        /**
         * 每个周期 x 移动的位置
         */
        float ratioX;
        /**
         * 每个周期 y 移动的位置
         */
        float ratioY;
        /**
         * 总共周期
         */
        long totalCount;
        /**
         * 当前周期
         */
        long count = 0;
        int period = NORMAL;

        CoordBean(float startX, float startY, float endX, float endY, long duration, int speed) {
            this.startX = startX;
            this.startY = startY;
            this.endX = endX;
            this.endY = endY;
            this.period = speed;
            totalCount = duration / speed;
            ratioX = (endX - startX) / totalCount;
            ratioY = (endY - startY) / totalCount;
        }
    }

}
